package handler

import (
	"fmt"
	"gitee.com/DonHz/cloud-file-storage/cache"
	"gitee.com/DonHz/cloud-file-storage/db"
	"gitee.com/DonHz/cloud-file-storage/util"
	"github.com/gin-gonic/gin"
	"github.com/gomodule/redigo/redis"
	"net/http"
	"os"
	"strconv"
	"strings"
	"time"
)

// 分块上传
type MultipartUploadInfo struct {
	FileHash   string
	FileSize   int
	UploadID   string // 分块上传的唯一ID
	ChunkSize  int    // 分块大小
	ChunkCount int    // 分块数量
}

func InitialMultipartUploadHandler(c *gin.Context) {
	// 1. 解析用户请求参数
	c.Request.ParseForm()
	username := c.Request.Form.Get("username")
	filehash := c.Request.Form.Get("filehash")
	filesize, err := strconv.Atoi(c.Request.Form.Get("filesize"))
	if err != nil {
		fmt.Println("Failed to get filesize, error：", err)
		return
	}
	// 2. 获得redis的一个连接
	rConn := cache.RedisPool().Get()
	defer rConn.Close()
	// 3.生成分块上传的初始化信息
	upInfo := MultipartUploadInfo{
		FileHash:   filehash,
		FileSize:   filesize,
		UploadID:   username + fmt.Sprintf("%x", time.Now().UnixNano()),
		ChunkSize:  5 * 1024 * 1024, // 5MB
		ChunkCount: filesize/(5*1024*1024) + 1,
	}
	// 4. 将初始化信息写入到redis缓存
	rConn.Do("HSET", "MP_"+upInfo.UploadID, "chunkcount", upInfo.ChunkCount)
	rConn.Do("HSET", "MP_"+upInfo.UploadID, "filehash", upInfo.FileHash)
	rConn.Do("HSET", "MP_"+upInfo.UploadID, "filesize", upInfo.FileSize)
	// 5. 将响应初始化数据返回到客户端
	c.Writer.Write(util.NewRespMsg(0, "OK", upInfo).JSONBytes())
	c.JSON(http.StatusOK, upInfo)
}

func UploadPartHandler(c *gin.Context) {
	// 1.解析请求参数
	c.Request.ParseForm()
	username := c.Request.Form.Get("username")
	uploadID := c.Request.Form.Get("uploadid")
	chunkIndex := c.Request.Form.Get("index")
	// 2.获得redis连接池中的一个连接
	rConn := cache.RedisPool().Get()
	defer rConn.Close()
	// 3. 获得文件句柄，用于存储分块内容
	fpath := "./data/" + username + "/" + uploadID + "/"
	f := "./data/" + username + "/" + uploadID + "/" + chunkIndex
	err := os.MkdirAll(fpath, 0744)
	if err != nil {
		fmt.Println("Failed to create dir, error：", err)
		return
	}
	fd, err := os.Create(f)
	if err != nil {
		fmt.Println("Failed to create file, error：", err)
		return
	}
	defer fd.Close()
	buf := make([]byte, 1024*1024) // 1MB
	// 这里可以优化一下，服务器计算文件的sha1值，然后和客户端传过来的sha1值进行比较
	// 如果不一致，就返回错误，否则就将分块内容写入到文件中
	// 4. 读取文件内容并写入到分块文件中
	for {
		n, err := c.Request.Body.Read(buf)
		fd.Write(buf[:n])
		if err != nil {
			break
		}
	}
	// 6. 更新redis缓存状态
	rConn.Do("HSET", "MP_"+uploadID, "chkidx_"+chunkIndex, 1)
	// 7. 返回处理结果到客户端
	c.JSON(http.StatusOK, "OK")
}

func CompleteUploadHandler(c *gin.Context) {
	// 1. 解析请求参数
	c.Request.ParseForm()
	username := c.Request.Form.Get("username")
	uploadID := c.Request.Form.Get("uploadid")
	filehash := c.Request.Form.Get("filehash")
	filesizeStr := c.Request.Form.Get("filesize")
	filesize, _ := strconv.Atoi(filesizeStr)
	filename := c.PostForm("filename")
	// 2. 获得redis连接池中的一个连接
	rConn := cache.RedisPool().Get()
	defer rConn.Close()
	// 3. 通过uploadid查询redis并判断是否所有分块上传完成
	data, err := redis.Values(rConn.Do("HGETALL", "MP_"+uploadID))
	if err != nil {
		fmt.Println("Failed to get data from redis, error：", err)
		c.JSON(http.StatusOK, "get data from redis failed")
		return
	}
	totalCount := 0
	chunkCount := 0
	for i := 0; i < len(data); i += 2 {
		k := string(data[i].([]byte))
		v := string(data[i+1].([]byte))
		if k == "chunkcount" {
			totalCount, _ = strconv.Atoi(v)
		} else if strings.HasPrefix(k, "chkidx_") && v == "1" {
			chunkCount++
		}
	}
	if totalCount != chunkCount {
		fmt.Println("Failed to merge chunk, totalCount：", totalCount, "chunkCount：", chunkCount)
		c.JSON(http.StatusOK, "merge chunk failed")
		return
	}
	// 4. TODO 合并分块
	// 5. 将文件信息写入到用户文件表中
	ok := db.OnUserFileUploadFinished(username, filehash, filename, int64(filesize))
	if !ok {
		fmt.Println("Failed to update user file table")
		c.JSON(http.StatusOK, "update user file table failed")
		return
	}
	// 6. 将文件信息写入到文件表中
	ok = db.OnFileUploadFinished(filehash, filename, int64(filesize), "")
	if !ok {
		fmt.Println("Failed to update file table")
		c.JSON(http.StatusOK, "update file table failed")
		return
	}
	// 7. 返回处理结果到客户端
	c.JSON(http.StatusOK, "OK")
}

func CancelUploadPartHandler(c *gin.Context) {
	// 1. 解析请求参数
	c.Request.ParseForm()
	//username := c.Request.Form.Get("username")
	//uploadID := c.Request.Form.Get("uploadid")
	// 2. 获得redis连接池中的一个连接
	rConn := cache.RedisPool().Get()
	defer rConn.Close()
	// 3. 删除已存在的分块文件
	// 4. 删除redis缓存状态
	// 5. 更新mysql文件status
	// 4. 返回处理结果到客户端
	c.JSON(http.StatusOK, "OK")
}

func MultipartUploadStatusHandler(c *gin.Context) {
	// 检查分块上传状态是否有效
	// 获取分块初始化信息
	// 获取已上传的分块信息
	// 返回分块上传状态信息
	c.JSON(http.StatusOK, "OK")
}
